//
//  TestTerrain.cpp
//  NanoOSX
//
//  Created by Jeppe Nielsen on 9/22/13.
//  Copyright (c) 2013 Jeppe Nielsen. All rights reserved.
//

#include "Engine.hpp"
#include "GameState.hpp"
#include "GameWorld.hpp"
#include "RenderSystem.hpp"
#include "Touchable.hpp"
#include "Material.hpp"
#include "TextureComponent.hpp"
#include "FirstPersonMoverSystem.hpp"
#include "TouchSystem.hpp"
#include "Light.hpp"
#include "SpriteMeshSystem.hpp"
#include "LabelMeshSystem.hpp"
#include "TransformHierarchy.hpp"
#include "SpriteTextureSystem.hpp"
#include "Plane.hpp"
#include "Timer.hpp"
#include "ParticleUpdaterSystem.hpp"
#include "ParticleMeshUpdater.hpp"
#include "TerrainSystem.hpp"

using namespace Nano;

class TestTerrain : public GameState {
    void Initialized() {
        renderTime = 0;
        renderCounter = 0;
        
        renderer = world.CreateSystem<RenderSystem>();
        world.CreateSystem<FirstPersonMoverSystem>()->Input = &Input;
        world.CreateSystem<ParticleUpdaterSystem>();
        world.CreateSystem<ParticleMeshUpdater>();
        world.CreateSystem<TransformHierarchy>();
        world.CreateSystem<TerrainSystem>();
        world.CreateSystem<TouchSystem>()->Input = &Input;
        
        // atlas = world.CreateObject();
        // atlas->AddComponent<Atlas>()->Load("images.xml", 1024);
        // atlas->AddComponent<TextureComponent>()->Texture().LoadFromPng("images.png");
        
        GameObject* texture = world.CreateObject();
        texture->AddComponent<TextureComponent>()->Texture().LoadFromPng("Assets/grass.png");
        
        GameObject* rock = world.CreateObject();
        rock->AddComponent<TextureComponent>()->Texture().LoadFromPng("Assets/rock.png");
        
        
        GameObject* camera = world.CreateObject();
        cam = camera->AddComponent<Camera>();
        cam->Viewport = Viewport();
        camera->AddComponent<Transform>()->Position = Vector3(0,0,4);
        camera->AddComponent<FirstPersonMover>();
        camera->GetComponent<Transform>()->Position = Vector3(64,32,64);
        camera->GetComponent<Transform>()->Rotation = Quaternion::LookAt(camera->GetComponent<Transform>()->Position, Vector3(64,0,32), Vector3(0,1,0));
        
        terrain = world.CreateObject();
        terrain->AddComponent<Transform>();
        terrain->AddComponent<Mesh>();
        terrain->AddComponent<Material>();
        terrain->AddComponent<Terrain>()->Textures[0] = rock->GetComponent<TextureComponent>();
        terrain->GetComponent<Terrain>()->AddHill(64, 64, 16, 8.0f);
        terrain->GetComponent<Terrain>()->RandomBumps(0, 13.0f);
        terrain->GetComponent<Terrain>()->Smooth(8);
        terrain->AddComponent<TextureComponent>(texture);
        terrain->AddComponent<Touchable>()->Down += event_handler(this, &TestTerrain::TerrainDown);
        terrain->GetComponent<Touchable>()->Up += event_handler(this, &TestTerrain::TerrainUp);
        
        cursor = world.CreateObject();
        cursor->AddComponent<Mesh>()->AddGeoSphere(0, 1.0f, 12);
        cursor->AddComponent<Material>();
        cursor->AddComponent<Transform>()->Scale = Vector3(1.0f,0.1f,1.0f);
        
        isPainting = false;
        
        timer = 0;
        lastTime = 0;
        
        GameObject* water = world.CreateObject();
        water->AddComponent<Transform>()->Position = Vector3(0,5,0);
        water->AddComponent<Mesh>()->AddPlane(Vector3(64,0,64), 128, Box(0,0,100,100));
        water->GetComponent<Mesh>()->SetColor(Colour(0.0f,0.0f,1.0f,0.5f));
        water->AddComponent<Material>()->BlendMode = BlendMode::Alpha;
        
        Input.TouchDown += event_handler(this, &TestTerrain::TouchDown);
    }
    
    float lastTime;
    
    void TouchDown(TouchEvent e) {
        if (e.Index!=0) return;
        bool doubleClick = (timer - lastTime)<0.3f;
        lastTime = timer;
        
        if (doubleClick) {
            isPainting = false;
            cam->Owner()->EnableComponent<FirstPersonMover>(!cam->Owner()->IsComponentEnabled<FirstPersonMover>());
        }
    }
    
    bool isPainting;
    int touchIndex;
    
    void TerrainDown(TouchData d) {
        touchIndex = d.Index;
        
        isPainting = true;
        
    }
    
    void TerrainUp(TouchData d) {
        isPainting = false;
    }
    
    void Update(float dt) {
        
        timer+= dt;
        
        Ray ray = cam->GetRay(Input.GetTouchPosition(0));
        
        float distance;
        float u,v;
        unsigned int tri;
        Vector3 normal;
        
        if (terrain->GetComponent<Terrain>()->IntersectsRay(ray, &distance, &u, &v, &tri, &normal)) {
            Vector3 left = cursor->GetComponent<Transform>()->World.GetValue()->TransformVector(Vector3(1,0,0));
            Vector3 forward = left.Cross(normal);
            
            cursor->GetComponent<Transform>()->Rotation = Quaternion::LookAt(0, forward, normal);
            cursor->GetComponent<Transform>()->Position = ray.position + ray.direction * distance + normal * 0.1f;
            
            if (isPainting && !cam->Owner()->IsComponentEnabled<FirstPersonMover>()) {
                Terrain* t = terrain->GetComponent<Terrain>();
                //t->AddHill(tri % t->xSegments, tri / t->xSegments, 10, touchIndex == 0 ?  0.1f : -0.1f);
                
                t->TextureCircle(tri % t->xSegments, tri / t->xSegments, 10, touchIndex == 0 ?  0.1f : -0.1f,0,0,0);
                
            }
            
        }
        
        
        world.Update(dt);
    }
    
    void Render() {
        glClearColor( 0.0f, 0.0f, 0.0f, 1.0f );
        glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        glEnable(GL_CULL_FACE);
        renderTimer.Begin();
        renderer->Render();
        renderTime += renderTimer.End();
        renderCounter++;
        if (renderCounter>20) {
            renderTime /= renderCounter;
            cout<<"Render() time = " << (1.0 / renderTime) << endl;
            renderCounter=0;
        }
    }
    
    GameObject* cube;
    GameWorld world;
    RenderSystem* renderer;
    Camera* cam;
    Timer renderTimer;
    double renderTime;
    int renderCounter;
    GameObject* pivot;
    GameObject* terrain;
    GameObject* cursor;
    float timer;
};


int main() {
    Engine e;
    e.Start<TestTerrain>();
}




